from dash import dcc, html, Input, Output
import dash
import dash_bootstrap_components as dbc
import requests
import random
import pytz
from datetime import datetime
import pandas as pd
import plotly.express as px
import random
from collections import defaultdict
historical_rates = defaultdict(list)
# Initialize Dash App with Bootstrap for styling
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.title = "Live Currency Exchange Dashboard"
app.config.suppress_callback_exceptions = True
# API Endpoint for Currency Rates
API_URL = "https://open.er-api.com/v6/latest"
# Country and Currency Mapping (Partial List)
COUNTRY_CURRENCY_MAPPING = {
"USD": "United States Dollar",
"EUR": "Eurozone Euro",
"GBP": "United Kingdom Pound",
"JPY": "Japan Yen",
"CAD": "Canada Dollar",
"AUD": "Australia Dollar",
"INR": "India Rupee",
"CNY": "China Yuan",
"CHF": "Switzerland Franc",
"ZAR": "South Africa Rand",
"PHP": "Philippine Peso",
"TRY": "Turkish Lira",
}
# Layout
app.layout = dbc.Container([
dbc.Row([
dbc.Col([
html.Div([
html.H2("Navigation", style={'color': '#C0C8CA', 'textAlign': 'center'}),
html.Button("Home", id="nav-home", n_clicks=0, style={
'width': '100%', 'padding': '10px', 'margin-bottom': '10px',
'background-color': '#243B55', 'color': '#C0C8CA', 'border': 'none'
}),
html.Button("Live Exchange", id="nav-exchange", n_clicks=0, style={
'width': '100%', 'padding': '10px', 'margin-bottom': '10px',
'background-color': '#243B55', 'color': '#C0C8CA', 'border': 'none'
}),
html.Button("Monthly Rates", id="nav-monthly", n_clicks=0, style={
'width': '100%', 'padding': '10px', 'margin-bottom': '10px',
'background-color': '#243B55', 'color': '#C0C8CA', 'border': 'none'
}),
], style={'background-color': '#0D1B2A', 'height': '100vh', 'padding': '20px'})
], width=2),
dbc.Col([
html.Div(id='main-content', style={'margin-top': '20px'}),
], width=10),
]),
dcc.Interval(id='interval-component', interval=1000, n_intervals=0)
], fluid=True, style={'background-color': '#0D1B2A'})
# Content Layouts
import pytz
from datetime import datetime
def home_layout():
return html.Div([
html.H1("Welcome to the USD Currency Exchange Dashboard",
style={'color': '#C0C8CA', 'textAlign': 'center', 'margin-bottom': '20px'}),
html.P("Use the navigation to explore live rates and monthly trends.",
style={'color': '#AAB7B7', 'textAlign': 'center', 'margin-bottom': '40px'}),
html.H3("Global Time Zones",
style={'color': '#C0C8CA', 'textAlign': 'center', 'margin-bottom': '20px'}),
# Grid for time zones
html.Div(id='live-time-grid', style={
'display': 'grid',
'grid-template-columns': 'repeat(auto-fit, minmax(200px, 1fr))',
'gap': '20px',
'padding': '20px',
'background-color': '#1B263B',
'border-radius': '10px',
}),
# Interval for updating time every second
dcc.Interval(id='time-interval', interval=1000, n_intervals=0)
], style={'background-color': '#0D1B2A', 'padding': '20px'})
@app.callback(
Output('live-time-grid', 'children'),
Input('time-interval', 'n_intervals')
)
def update_live_time(n_intervals):
# Time zones to display
time_zones = [
"UTC", "US/Eastern", "US/Central", "US/Mountain", "US/Pacific",
"Europe/London", "Europe/Paris", "Europe/Berlin", "Europe/Moscow",
"Asia/Tokyo", "Asia/Shanghai", "Asia/Kolkata", "Asia/Dubai",
"Australia/Sydney", "Australia/Perth", "Africa/Johannesburg",
"Africa/Cairo", "America/Sao_Paulo", "America/Mexico_City",
"America/Buenos_Aires", "America/Vancouver",
"America/Toronto", "Pacific/Auckland", "Pacific/Honolulu"
]
# Fetch the current time for each time zone
time_list = []
for tz in time_zones:
now = datetime.now(pytz.timezone(tz))
time_list.append(html.Div([
html.H4(f"{tz}", style={'color': '#FFD700', 'text-align': 'center', 'margin-bottom': '10px'}),
html.P(f"{now.strftime('%Y-%m-%d %H:%M:%S')}",
style={'color': '#FFFFFF', 'text-align': 'center', 'font-size': '18px', 'margin': '0'})
], style={
'padding': '20px',
'background-color': '#243B55',
'border-radius': '10px',
'box-shadow': '0px 4px 6px rgba(0, 0, 0, 0.1)',
}))
return time_list
def live_exchange_layout():
return html.Div([
dbc.Row([
dbc.Col([
dcc.Dropdown(
id='base-currency',
options=[],
placeholder="Select Base Currency",
style={
'margin-bottom': '10px',
'background-color': '#243B55',
'color': '#C0C8CA',
'border-radius': '5px',
'width': '100%'
},
),
dcc.Dropdown(
id='target-currency',
options=[],
placeholder="Select Target Currency",
style={
'margin-bottom': '20px',
'background-color': '#243B55',
'color': '#C0C8CA',
'border-radius': '5px',
'width': '100%'
}
),
html.Div([
dcc.Input(
id='amount',
type='number',
placeholder="Enter Amount",
style={
'margin-bottom': '10px',
'background-color': '#243B55',
'color': '#C0C8CA',
'padding': '10px',
'border-radius': '5px',
'width': '100%'
}
),
html.Div(id='conversion-result', style={
'color': '#C0C8CA',
'textAlign': 'center',
'margin-top': '10px',
'font-size': '18px',
'font-weight': 'bold'
})
], style={'margin-bottom': '20px'}),
], width=12),
]),
html.H3("Global Currency Rates", style={'color': '#C0C8CA', 'textAlign': 'center', 'margin-bottom': '20px'}),
html.Div([
html.Div([
html.Div("Currency", style={'font-weight': 'bold', 'color': '#FFD700', 'text-align': 'left'}),
html.Div("Rate", style={'font-weight': 'bold', 'color': '#FFD700', 'text-align': 'right'}),
], style={
'display': 'grid',
'grid-template-columns': '1fr 1fr',
'padding': '10px',
'background-color': '#243B55',
'border-bottom': '2px solid #1B263B',
}),
html.Div(id='global-currency-list', style={
'color': '#C0C8CA',
'overflow-y': 'scroll',
'height': '500px',
'padding': '10px',
'background-color': '#1B263B',
'border-radius': '10px',
'font-size': '18px',
'text-align': 'left',
}),
], style={'padding': '10px'}),
])
def monthly_rates_layout():
return html.Div([
html.H3("Monthly Exchange Rate Trends",
style={'color': '#C0C8CA', 'textAlign': 'center', 'margin-bottom': '20px'}),
dcc.Graph(
id='monthly-rates-line-bar-graph',
style={'background-color': '#1B263B', 'border-radius': '10px'}
),
html.Div([
dcc.Dropdown(
id='currency-selection',
options=[{'label': v, 'value': k} for k, v in COUNTRY_CURRENCY_MAPPING.items()],
placeholder="Select Currency",
style={
'margin': '0 auto',
'width': '60%',
'background-color': '#243B55',
'color': '#C0C8CA',
'border-radius': '5px',
'textAlign': 'center',
},
),
], style={'display': 'flex', 'justify-content': 'center', 'margin-bottom': '20px'}),
html.Div([
dcc.RadioItems(
id='time-range-selection',
options=[
{'label': '7 Days', 'value': 7},
{'label': '15 Days', 'value': 15},
{'label': '30 Days', 'value': 30}
],
value=30,
labelStyle={
'display': 'inline-block',
'margin': '10px',
'color': '#C0C8CA',
'font-weight': 'bold'
},
),
], style={'textAlign': 'center'}),
dcc.Interval(id='monthly-update-interval', interval=1000, n_intervals=0), # Update every 1 second
], style={
'background-color': '#0D1B2A',
'padding': '20px',
'border-radius': '10px'
})
@app.callback(
Output('monthly-rates-line-bar-graph', 'figure'),
[
Input('currency-selection', 'value'),
Input('monthly-update-interval', 'n_intervals'),
]
)
def update_infinite_graph(selected_currency, n_intervals):
if not selected_currency:
return px.line(
title="Please select a currency to view live fluctuations",
labels={'x': 'Time', 'y': 'Rate'}
)
try:
# Fetch rate from API
response = requests.get(f"{API_URL}/USD")
data = response.json()
rates = data.get('rates', {})
base_rate = rates.get(selected_currency)
if not base_rate:
return px.line(
title=f"Rate for {selected_currency} not available.",
labels={'x': 'Time', 'y': 'Rate'}
)
# Simulate live fluctuations
fluctuated_rate = base_rate + random.uniform(-0.05, 0.05)
# Update historical
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
historical_rates[selected_currency].append({'time': now, 'rate': fluctuated_rate})
# Create DataFrame
df = pd.DataFrame(historical_rates[selected_currency])
fig = px.line(
df,
x='time',
y='rate',
title=f"Live Exchange Rate for {COUNTRY_CURRENCY_MAPPING[selected_currency]} (USD)",
labels={'time': 'Time', 'rate': 'Rate'},
)
fig.update_layout(
paper_bgcolor='#1B263B',
plot_bgcolor='#1B263B',
font_color='#C0C8CA',
xaxis=dict(title_font=dict(size=14), tickangle=45),
yaxis=dict(title_font=dict(size=14)),
showlegend=False,
)
fig.update_traces(
line=dict(color='#FFD700', width=2),
marker=dict(size=4, color='#FFD700', symbol='circle')
)
return fig
except Exception as e:
return px.line(title=f"Error fetching data: {str(e)}")
# Callbacks
@app.callback(
Output('main-content', 'children'),
[Input('nav-home', 'n_clicks'),
Input('nav-exchange', 'n_clicks'),
Input('nav-monthly', 'n_clicks')]
)
def update_main_content(home_clicks, exchange_clicks, monthly_clicks):
ctx = dash.callback_context
if not ctx.triggered:
return home_layout()
button_id = ctx.triggered[0]['prop_id'].split('.')[0]
if button_id == 'nav-home':
return home_layout()
elif button_id == 'nav-exchange':
return live_exchange_layout()
elif button_id == 'nav-monthly':
return monthly_rates_layout()
return home_layout()
@app.callback(
[Output('global-currency-list', 'children'),
Output('base-currency', 'options'),
Output('target-currency', 'options')],
[Input('interval-component', 'n_intervals')]
)
def update_global_currency_list(n):
try:
response = requests.get(f"{API_URL}/USD")
data = response.json()
rates = data.get('rates', {})
fluctuating_rates = {
currency: rate + random.uniform(-0.05, 0.05) for currency, rate in rates.items()
}
sorted_rates = sorted(fluctuating_rates.items(), key=lambda x: x[1], reverse=False)
rate_elements = []
for idx, (currency, rate) in enumerate(sorted_rates):
row_style = {
'display': 'grid',
'grid-template-columns': '1fr 1fr',
'padding': '10px',
'background-color': '#243B55' if idx % 2 == 0 else '#1B263B',
'border-bottom': '1px solid #1B263B',
'border-radius': '5px',
}
rate_elements.append(html.Div([
html.Div(f"{COUNTRY_CURRENCY_MAPPING.get(currency, currency)} ({currency})",
style={'text-align': 'left', 'font-weight': 'bold'}),
html.Div(f"{round(rate, 2)}", style={'text-align': 'right'}),
], style=row_style))
options = [{'label': f"{COUNTRY_CURRENCY_MAPPING.get(currency, currency)} ({currency})", 'value': currency}
for currency in rates.keys()]
return rate_elements, options, options
except Exception as e:
return [html.P(f"Error fetching data: {str(e)}", style={'color': 'red'})], [], []
@app.callback(
Output('conversion-result', 'children'),
[Input('base-currency', 'value'),
Input('target-currency', 'value'),
Input('amount', 'value')]
)
def convert_currency(base_currency, target_currency, amount):
if not base_currency or not target_currency or not amount:
return "Please select currencies and enter an amount."
if base_currency == target_currency:
return f"{amount} {base_currency} = {amount} {target_currency}"
try:
response = requests.get(f"{API_URL}/{base_currency}")
data = response.json()
rate = data.get('rates', {}).get(target_currency)
if rate:
converted_amount = round(amount * rate, 2)
return f"{amount} {base_currency} = {converted_amount} {target_currency}"
return "Conversion rate not available."
except Exception as e:
return f"Error converting currency: {str(e)}"
if __name__ == '__main__':
app.run_server(debug=True)